
    sgvizler.ui = (function () {

        //// #id's to html elements:
        var id = {
            script:       'sgvzlr_script',      // #id to the script tag for this file
            chartCon:     'sgvzlr_gchart',      // #id to the container to hold the chart
            queryForm:    'sgvzlr_formQuery',   //
            queryTxt:     'sgvzlr_cQuery',      // query text area.
            formQuery:    'sgvzlr_strQuery',    // hidden query string. "trick" taken from snorql.
            formEndpoint: 'sgvzlr_strEndpoint', //
            formFormat:   'sgvzlr_btnFormat',   //
            formWidth:    'sgvzlr_strWidth',    //
            formHeight:   'sgvzlr_strHeight',   //
            formChart:    'sgvzlr_optChart',    //
            prefixCon:    'sgvzlr_cPrefix',     // print prefixes
            messageCon:   'sgvzlr_cMessage'     // print messages
        },

            attr = {
                prefix:      'data-sgvizler-',
                prefixChart: 'data-sgvizler-chart-options',

                valueAssign: '=',
                valueSplit:  '|'
            },

            permissible_params = [ 'query', 'endpoint', 'format', 'chart', 'width', 'height' ], // permissible URL parameters.

            html = {
                a: function (href, link, attr) {
                    var txt = "";
                    if (attr === undefined) { attr = ""; }
                    if (href !== undefined && link !== undefined) {
                        txt = "<a " + attr + " href='" + href + "'>" + link + "</a>";
                    }
                    return txt;
                },
                ul: function () {
                    var i,
                        txt = "";
                    if (arguments.length) {
                        txt = "<ul>";
                        for (i = 0; i < arguments.length; i += 1) {
                            txt += "<li>" + arguments[i] + "</li>";
                        }
                        txt += "</ul>";
                    }
                    return txt;
                },
                tag: function (tag, content, attr) {
                    if (attr === undefined) {
                        attr = "";
                    } else { attr = " " + attr; }
                    return "<" + tag + attr + ">" + content + "</" + tag + ">";
                }
            },

            isElement = function (elementID) {
                return $('#' + elementID).length > 0;
            },

            setElementValue = function (elementID, value) {
                if (isElement(elementID)) {
                    $('#' + elementID).val(value);
                }
            },
            setElementText = function (elementID, value) {
                if (isElement(elementID)) {
                    $('#' + elementID).text(value);
                }
            },
            setElementHTML = function (elementID, value) {
                if (isElement(elementID)) {
                    $('#' + elementID).html(value);
                }
            },

            displayPrefixes = function () {
                setElementText(id.prefixCon, sgvizler.query.prototype.getPrefixes());
            },
            displayUserInput = function (queryOpt) {
                setElementValue(id.queryTxt, queryOpt.query);
                setElementValue(id.formEndpoint, queryOpt.endpoint);
                $('input:radio[id=' + id.formFormat + '][value=' + queryOpt.endpoint_output + ']').attr('checked', true);
                setElementValue(id.formChart, queryOpt.chart);
                setElementValue(id.formWidth, queryOpt.chartOptions.width);
                setElementValue(id.formHeight, queryOpt.chartOptions.height);
            },
            displayChartTypesMenu = function () {
                var lib = sgvizler.charts.library,
                    chart;
                if (isElement(id.formChart)) {
                    for (chart in lib) {
                        if (lib.hasOwnProperty(chart)) {
                            $('#' + id.formChart)
                                .append($('<option/>')
                                        .val(chart)
                                        .html(chart));
                        }
                    }
                }
            },

            displayUI = function (queryOpt) {
                displayPrefixes();
                displayChartTypesMenu();
                displayUserInput(queryOpt);
            },

            displayFeedback = function (queryOpt, messageName) {
                var message,
                    homepage = "http://code.google.com/p/sgvizler",
                    container = queryOpt.container;
                if (queryOpt.container === id.chartCon && isElement(id.messageCon)) {
                    container = id.messageCon;
                }

                if (queryOpt.loglevel === 0) {
                    message = "";
                } else if (queryOpt.loglevel === 1) {
                    if (messageName === "LOADING") {
                        message = "Loading...";
                    } else if (messageName === "ERROR_ENDPOINT" || messageName === "ERROR_UNKNOWN") {
                        message = "Error.";
                    }
                } else {
                    if (messageName === "LOADING") {
                        message = "Sending query ...";
                    } else if (messageName === "ERROR_ENDPOINT") {
                        message = "Error querying endpoint. Possible errors:" +
                            html.ul(
                                html.a(queryOpt.endpoint, "SPARQL endpoint") + " down? " +
                                    html.a(queryOpt.endpoint + queryOpt.endpoint_query_url + queryOpt.encodedQuery,
                                                "Check if query runs at the endpoint") + ".",
                                "Malformed SPARQL query? " +
                                    html.a(queryOpt.validator_query_url + queryOpt.encodedQuery, "Check if it validates") + ".",
                                "CORS supported and enabled? Read more about " +
                                    html.a(homepage + "/wiki/Compatibility", "CORS and compatibility") + ".",
                                "Is your " + html.a(homepage + "/wiki/Compatibility", "browser support") + "ed?",
                                "Hmm.. it might be a bug! Please file a report to " +
                                    html.a(homepage + "/issues/", "the issues") + "."
                            );
                    } else if (messageName === "ERROR_UNKNOWN") {
                        message = "Unknown error.";
                    } else if (messageName === "NO_RESULTS") {
                        message = "Query returned no results.";
                    } else if (messageName === "DRAWING") {
                        message = "Received " + queryOpt.noRows + " rows. Drawing chart...<br/>" +
                            html.a(queryOpt.endpoint + queryOpt.endpoint_query_url + queryOpt.encodedQuery,
                                        "View query results", "target='_blank'") + " (in new window).";
                    }
                }
                if (message) {
                    $('#' + container).append(html.tag("p", message));
                }
            },


            getQueryOptionAttr = function (elementID) {
                var i,
                    queryOpt = {},
                    elmAttrs = document.getElementById(elementID).attributes;
                for (i = 0; i < elmAttrs.length; i += 1) {
                    if (sgvizler.fn.startsWith(elmAttrs[i].name, attr.prefix)) {
                        queryOpt[elmAttrs[i].name.substring(attr.prefix.length)] = elmAttrs[i].value;
                    }
                }
                return queryOpt;
            },
            getChartOptionAttr = function (elementID) {
                var i,
                    options,
                    assignment,
                    path,
                    o,
                    j,
                    chartOpt = {},
                    attrValue = $("#" + elementID).attr(sgvizler.ui.attr.prefixChart);
                if (attrValue !== undefined) {
                    options = attrValue.split(attr.valueSplit);
                    for (i = 0; i < options.length; i += 1) {
                        assignment = options[i].split(attr.valueAssign);
                        path = assignment[0].split(".");
                        o = chartOpt;
                        for (j = 0; j < path.length - 1; j += 1) {
                            if (o[path[j]] === undefined) {
                                o[path[j]] = {};
                            }
                            o = o[path[j]];
                        }
                        o[path[j]] = assignment[1];
                    }
                }
                // get width and heigth from css. take only numbers.
                chartOpt.width = /(\d+)/.exec($("#" + elementID).css('width'))[1];
                chartOpt.height = /(\d+)/.exec($("#" + elementID).css('height'))[1];
                return chartOpt;
            },

            getUrlParams = function (params) {
                /*jslint regexp: true */
                var urlParams = {},
                    e,
                    r = /([^&=]+)=?([^&]*)/g, // parameter, value pairs.
                    d = function (s) { return decodeURIComponent(s.replace(/\+/g, " ")); }, // replace '+' with space.
                    q = window.location.search.substring(1); // URL query string part.

                // set permissible params.
                if (!params) { params = permissible_params; }

                while ((e = r.exec(q))) {
                    if (e[2].length > 0 && $.inArray(e[1], params) !== -1) {
                        urlParams[d(e[1])] = d(e[2]);
                    }
                }
                return urlParams;
            },

            resetPage = function () {
                document.location = sgvizler.option.home;
            },
            submitQuery = function () {
                $('#' + id.formQuery).val($('#' + id.queryTxt).val());
                $('#' + id.queryForm).submit();
            },

            waitingCharts = 0,
            loadingChart = function () {
                waitingCharts += 1;
                if (!$('body,html').css('cursor', 'progress')) {
                    $('body,html').css('cursor', 'progress');
                }
            },
            doneLoadingChart = function () {
                waitingCharts -= 1;
                if (waitingCharts === 0 && $('body,html').css('cursor', 'progress')) {
                    $('body,html').css('cursor', 'default');
                }
            },

            linkify = function (url, attr) {
                var prefixed = sgvizler.parser.prefixify(url),
                    link;
                if (prefixed !== url) {
                    link = html.a(url, prefixed, attr);
                } else {
                    link = url;
                }
                return link;
            };

        /////////////////////////////////////////////////////////////////
        // PUBLICs

        return {
            linkify: linkify,
            getUrlParams: getUrlParams,
            displayUI: displayUI,
            getQueryOptionAttr: getQueryOptionAttr,
            getChartOptionAttr: getChartOptionAttr,

            // TODO: make separate module Form?
            resetPage: resetPage,
            submitQuery: submitQuery,

            // TODO: private.
            isElement: isElement,

            // TODO: move to logger.
            loadingChart: loadingChart,
            doneLoadingChart: doneLoadingChart,
            displayFeedback: displayFeedback,

            // TODO: private.
            id: {
                script: id.script,
                chartCon: id.chartCon
            },
            attr: {
                prefix: attr.prefix,
                prefixChart: attr.prefixChart,
                valueSplit: attr.valueSplit
            }
        };

    }());

